/**
 *******************************************************************************
 * @file      pxx2_driver.c
 * @version   V1.0.0    
 * @date      2013.05.04
 * @brief     pxx pulse driver for Tananis on PA10.	
 *            TIM1_CH3
 *            DMA2_Stream6
 * @author    - Adela 
 *            - Robert Zhang <armner@gmail.com>
 *            - 
 */

#include "stm32f2xx.h"
#include "pxx_driver.h"

#include "FreeRTOS.h"
#include "semphr.h"

static xSemaphoreHandle pxx_sema=NULL;
static uint16_t pxx_stream[400];

int pxx2_open(Driver *driver)
{
    GPIO_InitTypeDef GPIO_InitStructure;
    int i;

    RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOA, ENABLE);

    GPIO_PinAFConfig(GPIOA, GPIO_PinSource10, GPIO_AF_TIM1);

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_10;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_AF;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);


    //create a flag for waiting
    vSemaphoreCreateBinary(pxx_sema);
    if(pxx_sema ==NULL)
    {
        return -1;
    }
    
    //get system clocks
    RCC_ClocksTypeDef RCC_Clocks;
        
    RCC_GetClocksFreq( &RCC_Clocks);
    
    for(i=0; i<400; i++)
    {
        pxx_stream[i] = i*20 +10;
    }
    
    RCC->APB2ENR |= RCC_APB2ENR_TIM1EN ;            // Enable clock
    RCC->AHB1ENR |= RCC_AHB1ENR_DMA2EN ;            // Enable DMA2 clock

    DMA_InitTypeDef DMA_InitStructure;

    DMA_DeInit(DMA2_Stream6);
    DMA_InitStructure.DMA_Channel = DMA_Channel_6;  
    DMA_InitStructure.DMA_PeripheralBaseAddr = (uint32_t)(TIM1->DMAR) ;
    DMA_InitStructure.DMA_Memory0BaseAddr = (uint32_t)pxx_stream;
    DMA_InitStructure.DMA_DIR = DMA_DIR_MemoryToPeripheral;
    DMA_InitStructure.DMA_BufferSize = 400;
    DMA_InitStructure.DMA_PeripheralInc = DMA_PeripheralInc_Disable;
    DMA_InitStructure.DMA_MemoryInc = DMA_MemoryInc_Enable;
    DMA_InitStructure.DMA_PeripheralDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_MemoryDataSize = DMA_PeripheralDataSize_HalfWord;
    DMA_InitStructure.DMA_Mode = DMA_Mode_Circular;
    DMA_InitStructure.DMA_Priority = DMA_Priority_High;
    DMA_InitStructure.DMA_FIFOMode = DMA_FIFOMode_Disable;
    DMA_InitStructure.DMA_FIFOThreshold = DMA_FIFOThreshold_Full;
    DMA_InitStructure.DMA_MemoryBurst = DMA_MemoryBurst_Single;
    DMA_InitStructure.DMA_PeripheralBurst = DMA_PeripheralBurst_Single;

    DMA_Init(DMA2_Stream6, &DMA_InitStructure);

    //config TIM1 as 9ms pulse driver
    TIM_TimeBaseInitTypeDef  TIM_TimeBaseStructure;
    TIM_OCInitTypeDef  TIM_OCInitStructure;    

    /* Time Base configuration */
    TIM_TimeBaseStructure.TIM_Prescaler = (RCC_Clocks.PCLK2_Frequency) / 2000000 - 1 ;
    TIM_TimeBaseStructure.TIM_CounterMode = TIM_CounterMode_Up;
    TIM_TimeBaseStructure.TIM_Period = 18000;
    TIM_TimeBaseStructure.TIM_ClockDivision = 0;
    TIM_TimeBaseStructure.TIM_RepetitionCounter = 3;

    TIM_TimeBaseInit(TIM1, &TIM_TimeBaseStructure);

    /* Channel 3 Configuration in PWM mode */
    TIM_OCInitStructure.TIM_OCMode = TIM_OCMode_Toggle;
    TIM_OCInitStructure.TIM_OutputState = TIM_OutputState_Enable;
    TIM_OCInitStructure.TIM_OutputNState = TIM_OutputNState_Enable;
    TIM_OCInitStructure.TIM_Pulse = pxx_stream[0];
    TIM_OCInitStructure.TIM_OCPolarity = TIM_OCPolarity_Low;
    TIM_OCInitStructure.TIM_OCNPolarity = TIM_OCNPolarity_Low;
    TIM_OCInitStructure.TIM_OCIdleState = TIM_OCIdleState_Set;
    TIM_OCInitStructure.TIM_OCNIdleState = TIM_OCIdleState_Reset;

    TIM_OC3Init(TIM1, &TIM_OCInitStructure);
    
    TIM_SetCompare2(TIM1, 15000);
    TIM_ITConfig(TIM1, TIM_IT_CC2, ENABLE);

    /* TIM1 counter enable */
    TIM_Cmd(TIM1, ENABLE);
  
    /* DMA enable*/
    DMA_Cmd(DMA2_Stream6, ENABLE);
    
    TIM_DMACmd(TIM1, TIM_DMA_CC3, ENABLE);

    /* Main Output Enable */
    TIM_CtrlPWMOutputs(TIM1, ENABLE);
  
    NVIC_EnableIRQ(TIM1_CC_IRQn) ;
    
    return 0;
}

int pxx2_write(Driver *driver, void *buffer, int len, OFFSET offset)
{
    
    return 0;
}


int pxx2_close(Driver *driver)
{
    GPIO_InitTypeDef GPIO_InitStructure;

    GPIO_InitStructure.GPIO_Pin = GPIO_Pin_7;
    GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;
    GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;
    GPIO_InitStructure.GPIO_OType = GPIO_OType_PP;
    GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;
    GPIO_Init(GPIOA, &GPIO_InitStructure);
 
    vSemaphoreDelete(pxx_sema);
    return 0;
}

void TIM1_CC_IRQHandler()
{
#include "led_driver.h"
    if(0)
    {
        static char t;

    t^=1;
    
    led_driver.write(&led_driver,&t,sizeof(t),0);
    }

    TIM1->DIER &= ~TIM_DIER_CC2IE ;         // stop this interrupt
    TIM1->SR &= ~TIM_SR_CC2IF ;                             // Clear flag

    DMA2_Stream6->CR &= ~DMA_SxCR_EN ;              // Disable DMA
    DMA2->HIFCR = DMA_HIFCR_CTCIF6 | DMA_HIFCR_CHTIF6 | DMA_HIFCR_CTEIF6 | DMA_HIFCR_CDMEIF6 | DMA_HIFCR_CFEIF6 ; // Write ones to clear bits
    DMA2_Stream6->M0AR = (uint32_t)(&pxx_stream[1]);
    DMA2_Stream6->CR |= DMA_SxCR_EN ;               // Enable DMA

    TIM1->CCR3 = pxx_stream[0];

    TIM1->DIER |= TIM_DIER_CC2IE ;  // Enable this interrupt
}


Driver pxx2_driver =
{
    &pxx2_open,
    &pxx2_write,
    NULL,
    NULL,
    &pxx2_close
};

